<?php

class Image implements ISingleton 
{
	/**
     * @var Image
     */
    protected static $_instance;
    
    /**
     * Конфиг картинок.
     * 
     * @var Config_Ini
     */
    protected $_config;
    
    /**
     * Класс-транспорт для картинок.
     * 
     * @var Image_ITransfer
     */
    protected $_transport;
    
    /**
     * @return Image
     */
    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }
    
    private function __construct()
    {
    	$config = new Config_Ini(BASE_DIR . '/config/image.ini');
    	$this->_config = $config;
    	
    	$class = "Image_Transport_" . ucfirst($config->options->transport);
    	$transport = new $class();
    	$this->_transport = $transport;
    }

    /**
     * Получение превьюшки.
     * Метод получает информацию об исходной картинке. И возвращает путь до закешированной картинки.
     * Чтобы сформировать путь - используется конфиг картинок, но формируется он по шаблону: 
     * /i/p/$type/$subtype/$firsh_2_symb_from_md5_hash/$id.$extension
     * 
     * Метод не делает обращения к БД для проверки.
     * 
     * @param integer $id ID из таблицы image
     * @param string $table Тип из таблицы image, название таблицы в которой превьюшка
     * @param string $column Колонка из таблицы
     * @param string $type Тип превьюшки
     * @return string 
     */
    public function getPreview($id, $table, $column, $type)
    {
		$config = $this->_config;
		
		$tableConfig = $config->$table;
		if (!$tableConfig) {
			throw new Image_Exception("Попытка получения превьюшки для несуществующей таблицы: `$table`");
		}
		
		$columnConfig = $tableConfig->$column;
		if (!$columnConfig) {
			throw new Image_Exception("Попытка получения превьюшки для `$table` с несуществующей колонкой: `$column`");
		}
		
		$typeConfig = $columnConfig->$type;
		if (!$typeConfig) {
			throw new Image_Exception("Попытка получения превьюшки для `$table`.`$column` c несуществующим типом: `$type`");
		}
		
		$hash = substr(md5($id), 0, 2);
		$extension = $typeConfig->ext ? $typeConfig->ext : 'jpg';
		$host = Config::getInstance()->default->imagehost;
		$path = $host . "/i/p/{$tableConfig->folder}/{$columnConfig->folder}/$type/$hash/$id.$extension";
		return $path;
    }
    
    /**
     * Загрузка картинки оригинала.
     * Метод получает ссылку на сторонний http сервер, скачиват оттуда картинку и добавлят запись в image.
     * Новая картинка кладется по пути:
     * /i/o/$type/$first_2_symb_from_md5_hash/$id.$extension
     * 
     * Метод делает проверку на то, есть ли уже оригинальная картинка в image.ext_src.
     * 
     * @param string $url Внешняя ссылка на картинку
     * @param string $table Тип контента, название таблицы
     * @param string $column Колонка из таблицы
     * @return array Массив по всеми параметрами ImageRow
     * @throws Image_Exception В случае, если не удалось вставить запись в БД
     */
    public function downloadOriginal($url, $table, $column)
    {
    	$config = $this->_config;
		$tableConfig = $config->$table;
		if (!$tableConfig) {
			throw new Image_Exception("Попытка скачивания для несуществующего типа контента: `$table`");
		}
		
    	$columnConfig = $tableConfig->$column;
		if (!$columnConfig) {
			throw new Image_Exception("Попытка загрузки в `$table` с несуществующим типом: `$column`");
		}
		
    	try {
    		$uri = Zend_Uri::factory($url);
    		
    		$rowExists = ImagePeer::getInstance()->fetchRow("`type` = '$table' and `ext_src` = '$uri'");
    		
    		$waiting = false;
    		
    		if ($rowExists) {
    			if ($rowExists->getSrc() == 'waiting') {
    				$waiting = true;
    			} else {
    				return $rowExists->toArray();
    			}
    		}
    	
    		// Определение расширения
    		$extension = self::getExtension($uri->getPath());

    		// Запрос на скачивание
    		$client = new Zend_Http_Client($uri);
    		$response = $client->request();
    		if (!$response->isSuccessful()) {
    			throw new Image_Exception("Ошибка при скачивании файла: $url ({$response->getStatus()})");
    		}
    		
    		if ($waiting) {
    			$row = $rowExists;
    		} else {
	    		// Сохранение картинки в базе
	    		$row = ImagePeer::getInstance()->createRow();
	    		$row->setType($table);
	    		$row->setSubType($column);
	    		$row->setSrc('waiting');
	    		$row->setExtSrc($url);
	    		$row->setProcessed(0);
	    		$row->save();
    		}
    		
    		$hash = substr(md5($row->getId()), 0, 2);
    		$dir = "/i/o/{$tableConfig->folder}/{$columnConfig->folder}/$hash";
    		$path = "$dir/{$row->getId()}.$extension";
    		
    		$this->_getTransport()->mkdir($dir);
    		
    		// Сохраняем скачанный файл временно, чтобы передать его транспорту
    		$tmpfile = tempnam('/tmp', 'Image_DownloadOriginal_');
    		if (!file_put_contents($tmpfile, $response->getBody())) {
    			throw new Image_Exception("Ошибка при записи файла в $path");
    		}
    		
    		$this->_getTransport()->save($tmpfile, $path);
    		
    		$row->setSrc($path);
    		$row->save();
    		
    		return $row->toArray();
    		
    	} catch (Zend_Uri_Exception $e) {
    		throw new Image_Exception($e->getMessage());
    	} catch (Zend_Http_Exception $e) {
    		throw new Image_Exception($e->getMessage());
    	}
    }
    
    /**
     * Метод используется для того, чтобы добавить в таблицу оригиналов [загруженную] локальную картинку.
     * Метод будет вызываться из админки, она будет дергать его, передавая путь до файла и имя таблицы и получая в ответ ID,
     * а на нашей стороне файл положится куда нужно и сохранится в базу.
     * 
     * @param string $path Локальный полный путь до картинки
     * @param string $table Имя таблицы куда будет загружена картинка
     * @param string $column Имя колонки, в которую будет загружена картинка
     * @param string $forceNew Если параметр 1, то загрузчик будет сначала проверять, была ли загружена картинка с таким внешним путём и возвращать её
     * @return integer
     */
    public function uploadOriginal($path, $table, $column, $forceNew = 0)
    {
    	if (!file_exists($path)) {
    		throw new Image_Exception("Исходный файл не существует: $path");
    	}
    	
        $config = $this->_config;
		$tableConfig = $config->$table;
		if (!$tableConfig) {
			throw new Image_Exception("Попытка загрузки для несуществующего типа контента: `$table`");
		}
		
        $columnConfig = $tableConfig->$column;
		if (!$columnConfig) {
			throw new Image_Exception("Попытка загрузки в `$table` с несуществующим типом: `$column`");
		}
		
		if (!$forceNew) {
			// Проверка, есть ли в базе уже такая картинка с внешним путём
			$select = ImagePeer::getInstance()->select();
			$select->where('ext_src = ?', $path);
			$row = ImagePeer::getInstance()->fetchRow($select);
			if ($row) {
				// Если запись в базе уже есть, то сразу возвращаем её
				return $row->getId();
			}
		}
		
   		// Сохранение картинки в базе
   		$row = ImagePeer::getInstance()->createRow();
   		$row->setType($table);
   		$row->setSubType($column);
   		$row->setSrc('waiting');
   		$row->setExtSrc($path);
   		$row->setProcessed(0);
   		$row->save();
   		
   		$hash = substr(md5($row->getId()), 0, 2);
   		$extension = self::getExtension($path);

   		
		$dir = "/i/o/{$tableConfig->folder}/{$columnConfig->folder}/$hash";
		$newPath = "$dir/{$row->getId()}.$extension";
		
		$this->_getTransport()->mkdir($dir);
		$this->_getTransport()->save($path, $newPath);
		
   		$row->setSrc($newPath);
   		$row->save();
   		
   		return $row->getId();
    }
    
    
    /**
     * Метод используется для создания сконвертированного изображения (превьюшки).
     * Использует внутри себя метод getPreview() чтобы получить путь до превьюшки.
     * Использует команду для конвертации, которая описана в конфиге.
     * 
     * @param ImageRow $row
     * @param string $type Генерить только с указанным типом
     * @param boolean $force Перезаписывать существующие превьюшки
     * @return array Пути до всех сгенеренных картинок
     */
    public function makePreview(ImageRow $row, $type = null)
    {
	if ($row->getSrc() == 'waiting') {
		throw new Image_Exception('Попытка получения превьюшки для картинки со статусом waiting: ' . $row->getId());
	}

    	$table = $row->getType();
    	$column = $row->getSubtype();
    	
    	$config = $this->_config;
		$tableConfig = $config->$table;
		if (!$tableConfig) {
			throw new Image_Exception("Попытка получения превьюшки для несуществующей таблицы: `$table`");
		}
		
		$columnConfig = $tableConfig->$column;
		if (!$columnConfig) {
			throw new Image_Exception("Попытка получения превьюшки для `$table` с несуществующей колонкой: `$column`");
		}
		
		$return = array();
		foreach ($columnConfig as $name => $cmd) {
			if ($name == 'folder') continue;
			if (($type !== null) && ($name != $type)) continue;
			$cmd = $cmd->cmd;
	    		$path = $this->getPreview($row->getId(), $table, $column, $name);
			

	    		$this->_getTransport()->mkdir($path);
		
			$src = BASE_DIR . '/htdocs' . $row->getSrc();
			if (!file_exists($src)) {
				$src = 'http://172.17.2.121' . $row->getSrc();	
			}	    	
	    		$cmd = str_replace('$src', $src, $cmd);

			$previewPath = BASE_DIR . '/htdocs' . $path;
			if (file_exists($previewPath)) {
				echo "$previewPath --skip\n";
				continue;
			}
	    		
			$cmd = str_replace('$preview', BASE_DIR . '/htdocs' . $path, $cmd);
		    	$cmd = str_replace('$base_dir', BASE_DIR, $cmd);
		    	echo $cmd . "\n";
			exec($cmd);
			$this->_getTransport()->save(BASE_DIR . '/htdocs' . $path, $path);			
			$return[] = $path;
			
		}
		
		$row->setProcessed(1);
		$row->save();
		return $return;
    }
    
    /**
     * Принимает путь, возвращает расширение файла. Определяет регуляркой по последним символам.
     * 
     * @param string $path
     * @return string
     */
    public static function getExtension($path)
    {
   		preg_match('/\.([a-z]{2,4})$/', $path, $extension);
   		if (!isset($extension[1])) {
   			throw new Image_Exception("Ошибка при определении расширения скачиваемого файла: $path");
   		}
   		$extension = $extension[1];
   		return $extension;
    }
    
    /**
     * Возвращает класс, который управляет перемещениями файлов.
     * 
     * @return Image_ITransfer
     */
    protected function _getTransport()
    {
    	return $this->_transport;
    }
}
